package top.lixunda.commerce.common.core.utils;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author LiXunda
 * @version time:2019/10/14 9:10
 * <p>
 * 与Lambda表达式相关的一些工具
 */
@SuppressWarnings("Duplicates")
@Slf4j
public class MyLambdaUtils {

    private static final Map<Class<? extends EntityGetFunction>, String> CACHE_FIELD_NAME = new ConcurrentHashMap<>(1024);

    /**
     * 获取一个lambda方法对应的参数名称，仅支持set和get方法
     */
    @SuppressWarnings("unused")
    public static <T> String getLambdaName(Class<T> t, EntityGetFunction<T, ?> getMethod) {
        String name = CACHE_FIELD_NAME.get(getMethod.getClass());
        if (name == null) {
            SerializedLambda serializedLambda = getSerializedLambda(getMethod);
            String getMethodName = serializedLambda.getImplMethodName();
            name = String.valueOf(getMethodName.toCharArray()[3]).toLowerCase() + getMethodName.substring(4);
            name = getTableFieldName(name, serializedLambda);
            CACHE_FIELD_NAME.put(getMethod.getClass(), name);
        }
        return name;
    }

    /**
     * 获取一个lambda方法对应的参数名称，仅支持set和get方法
     */
    @SuppressWarnings("unused")
    public static <T> String getLambdaName(T t, EntityGetFunction<T, ?> getMethod) {
        String name = CACHE_FIELD_NAME.get(getMethod.getClass());
        if (name == null) {
            SerializedLambda serializedLambda = getSerializedLambda(getMethod);
            String getMethodName = serializedLambda.getImplMethodName();
            name = String.valueOf(getMethodName.toCharArray()[3]).toLowerCase() + getMethodName.substring(4);
            name = getTableFieldName(name, serializedLambda);
            CACHE_FIELD_NAME.put(getMethod.getClass(), name);
        }
        return name;
    }

    /**
     * 从serializedLambda中获取到该实体类的Class，然后获取对应name的属性，获取注解的值
     *
     * @param name             属性名称
     * @param serializedLambda serializedLambda
     * @return name属性上注解TableField或者注解TableId中的value值，该值标注了数据库中的字段名称
     */
    private static String getTableFieldName(String name, SerializedLambda serializedLambda) {
        Field field;
        try {
            field = serializedLambda.getInstantiatedMethodType().getDeclaredField(name);
        } catch (NoSuchFieldException e) {
            log.error("错误的get方法，找不到对应的属性");
            e.printStackTrace();
            return name;
        }
        if (field == null) {
            log.error("错误的get方法，找不到对应的属性");
            return name;
        }
        TableField tableField = field.getAnnotation(TableField.class);
        if (tableField != null) {
            return tableField.value();
        }
        TableId tableId = field.getAnnotation(TableId.class);
        if (tableId != null) {
            return tableId.value();
        }

        log.error("错误的get方法，找不到对应的属性");
        return name;
    }

    /**
     * 读取出来，变成一个序列化的Lambda对象
     *
     * @param lambda Lambda表达式，主要是泛型T中属性的get方法
     * @param <T>    实体类
     * @return SerializedLambda
     */
    private static <T> SerializedLambda getSerializedLambda(EntityGetFunction<T, ?> lambda) {
        SerializedLambda serializedLambda = null;
        try {
            ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(serialize(lambda))) {
                @Override
                protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
                    Class<?> clazz = super.resolveClass(objectStreamClass);
                    return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
                }
            };
            serializedLambda = (SerializedLambda) objIn.readObject();
        } catch (@SuppressWarnings("CatchMayIgnoreException") Exception ignored) {
            ignored.printStackTrace();
        }
        return serializedLambda;
    }

    /**
     * 序列化到一个byte数组中
     *
     * @param object 对象，这里是Lambda表达式
     * @return byte[]
     */
    private static byte[] serialize(Object object) {
        if (object == null) {
            return null;
        } else {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
            try {
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(object);
                oos.flush();
            } catch (IOException var3) {
                throw new IllegalArgumentException("Failed to serialize object of type: " + object.getClass(), var3);
            }

            return baos.toByteArray();
        }
    }

}
